package com.rybbaby.tss.core.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.rybbaby.tss.core.bean.BaseTemplateBean;
import com.rybbaby.tss.core.bean.EntityTemplateExtensionBean;
import com.rybbaby.tss.core.bean.TableColumnBean;
import com.rybbaby.tss.core.code.generate.config.GenerateConfig;
import com.rybbaby.tss.core.code.generate.view.bean.Views;
import com.rybbaby.tss.core.code.generate.view.util.ViewPropertiesReadUtil;
import org.apache.log4j.Logger;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.Version;

public class CodeGenerateUtil {
	
	private Logger log = Logger.getLogger(CodeGenerateUtil.class);
	
	private static Version version = null;
	private static Configuration freeMarkerCfg = null;
	private Template template = null;
	/**
	 * 生成参数
	 **/
	private GenerateConfig generateConfig;
	/**
	 * 模板参数
	 **/
	private Map<String , Object> parameters;
	
	public CodeGenerateUtil (){
		super();
		this.generateConfig = new GenerateConfig();
		CodeGenerateUtil.version = new Version("2.3.23");
		setFreeMarkerCfg(new Configuration(getVersion()));
	}
	
	public CodeGenerateUtil (GenerateConfig generateConfig ){
		this();
		this.generateConfig = generateConfig;
	}
	
	public CodeGenerateUtil (String projectDir , String templatesMapping , String module , String templateDir , String javaFileOutDir , String viewFileOutDir ){
		this();
		this.generateConfig.setProjectDir(projectDir);
		this.generateConfig.setTemplatesMapping(templatesMapping);
		this.generateConfig.setModule(module);
		this.generateConfig.setTemplateDir(templateDir);
		this.generateConfig.setJavaFileOutDir(javaFileOutDir);
		this.generateConfig.setViewFileOutDir(viewFileOutDir);
	}
	
	/**
	 * 生产代码文件
	 *
	 * @param columns
	 */
	public void generateCode(List<TableColumnBean> columns) {
		String module = this.generateConfig.getModule();// 模块名称
		String entityName = this.generateConfig.getEntityName();// 实体类名称
		String businessDescription = this.generateConfig.getBusinessDescription();// 功能描述
		String author = this.generateConfig.getAuthor();// 开发者姓名
		// 基础模块参数实体
		BaseTemplateBean baseTemplateBean = new BaseTemplateBean(module , entityName , businessDescription , author);
		// 数据库表名
		String tableName = this.generateConfig.getTableName();
		EntityTemplateExtensionBean table = new EntityTemplateExtensionBean(tableName , columns , this.generateConfig.getForeignKeys());// 实体类参数
		parameters = new HashMap<String , Object>();
		List<File> newFiles = new ArrayList<File>();
		boolean isUpdateViewProperties = false;
		String viewType = null;
		try {
			String [] templateMapping = this.generateConfig.getTemplatesMapping().split(",");
			File templateDir = new File(this.generateConfig.getTemplateDir());
			if(!templateDir.exists()) {
				throw new Exception("Not find template directory \"" + templateDir.getPath() + "\"");
			}
			String [] listFile = templateDir.list();
			List<String> realTemplates = Arrays.asList(listFile);
			Map<String , Map<String , Boolean>> genFielsMaps = this.generateConfig.getGenFielsMaps();
			for(String mappingStr : templateMapping) {
				isUpdateViewProperties = false;
				viewType = null;
				String [] mapping = mappingStr.split("->");
				if(mapping.length != 2) {
					continue;
				}
				String templateFileName = mapping[0];
				if(!realTemplates.contains(templateFileName)) {
					continue;
				}
				Boolean boolean1 = genFielsMaps.get(tableName).get(templateFileName);
				if(boolean1 == null || (boolean1 != null && !boolean1)) {
					log.info("跳过生成" + tableName + ":" + templateFileName);
					continue;
				}
				String fileName = null;
				String eName = baseTemplateBean.getEntityName();
				String firstLowerEName = StringUtils.converToJavaVariable(eName , true);
				String packageOrViewPageSuffix = StringUtils.replace(mapping[1] , "." , "\\");
				String newFileSaveDir = firstLowerEName + File.separator + packageOrViewPageSuffix;
				String outFilePath = null;
				if(templateFileName.endsWith(".java")) {
					outFilePath = this.generateConfig.getJavaFileOutDir();
				} else {
					outFilePath = this.generateConfig.getViewFileOutDir();
				}
				switch(templateFileName){
					case "IDao.java":
						fileName = "I" + eName + "Dao.java";
						break;
					case "IService.java":
						fileName = "I" + eName + "Service.java";
						break;
					case "Entity.java":
						fileName = eName + ".java";
						break;
					case "List.html":
						newFileSaveDir = firstLowerEName;
						isUpdateViewProperties = true;
						viewType = firstLowerEName + "ListView";
						fileName = firstLowerEName + StringUtils.converToJavaVariable(packageOrViewPageSuffix , false) + ".html";
						break;
					case "Edit.html":
						//弹层形式编辑
						newFileSaveDir = firstLowerEName;
						isUpdateViewProperties = true;
						viewType = firstLowerEName + "EditView";
						fileName = firstLowerEName + StringUtils.converToJavaVariable(packageOrViewPageSuffix , false) + ".html";
						break;
					default:
						fileName = eName + templateFileName;
				}
				outFilePath += File.separator + baseTemplateBean.getModulesName() + File.separator + newFileSaveDir + File.separator + fileName;
				outFilePath = StringUtils.replace(outFilePath , "\\\\" , "\\");
				log.info("正在生成文件：" + outFilePath);
				File newFile = writeFile(baseTemplateBean , table , outFilePath , templateFileName , columns);
				newFiles.add(newFile);
				if(isUpdateViewProperties) {
					updateViewProperties(module , entityName , viewType , outFilePath);
				}
			}
		} catch (Exception e) {
			log.error("生成代码过程发生异常" , e);
			if(newFiles.size() > 0) {
				log.info("即将删除生成的临时文件");
			}
			for(File file : newFiles) {
				log.info("正在删除文件：" + file.getPath());
				if(file != null && file.exists()) {
					if(!file.delete()) {
						log.info("文件删除失败:" + file.getPath());
					}
				}
			}
			e.printStackTrace();
		}
	}
	
	/**
	 * 更新视图配置文件
	 *
	 * @param module
	 *            所属模块
	 * @param viewType
	 *            视图类型
	 * @param outFilePath
	 *            文件输出路径
	 * @throws Exception
	 * @throws IOException
	 */
	private void updateViewProperties(String module , String entityName , String viewType , String outFilePath) throws Exception , IOException {
		String projectDir = this.generateConfig.getProjectDir();
		String viewMappingFilePath = new StringBuffer().append(projectDir).append(File.separator).append("resources").append(File.separator).append("view").append(File.separator).append("properties").append(File.separator).append(module).append("View.properties").toString();
		String viewMappingXmlFilePath = new StringBuffer().append(projectDir).append(File.separator).append("resources").append(File.separator).append("view").append(File.separator).append("xml").append(File.separator).append(module).append("View.xml").toString();
		try {
			String viewPath = StringUtils.substringAfterLast(outFilePath , projectDir + "\\webapp\\WEB-INF\\views\\");
			viewPath = StringUtils.replace(viewPath , "\\" , "/");
			Views views = ViewPropertiesReadUtil.parseViewXml(viewMappingXmlFilePath);
			if(StringUtils.isBlank(views.getModule())) {
				views.setModule(module);
			}
			if(StringUtils.isBlank(views.getDescribe())) {
				views.setDescribe("Controller的视图配置文件");
			}
			String controllerName = entityName + "Controller";
			String key = viewType;
			String describe = "设置" + viewType + "视图";
			String path = viewPath;
			views.addView(controllerName , key , describe , path);
			ViewPropertiesReadUtil.storeView(views , viewMappingFilePath);
			log.info("正在配置视图：" + viewType + " = " + viewPath);
		} catch (Exception e) {
			throw e;
		}
	}
	
	private File writeFile(BaseTemplateBean baseTemplateBean , EntityTemplateExtensionBean table , String outFilePath , String templateFileName , List<TableColumnBean> columns) throws Exception {
		OutputStreamWriter writer = null;
		FileOutputStream fos = null;
		File tempFile = null;
		File tempDir = null;
		try {
			tempFile = new File(outFilePath);
			if(!tempFile.exists()) {
				tempDir = tempFile.getParentFile();
				if(!tempDir.exists()) {
					tempDir.mkdirs();
				}
				tempFile.createNewFile();
			}
			fos = new FileOutputStream(tempFile);
			loadTemplateFile(templateFileName);
			parameters.put("baseInfo" , baseTemplateBean);// 基本信息：模块名称、实体名称、业务描述名称、开发者姓名、当前时间
			parameters.put("table" , table);// 数据库表名
			parameters.put("logicDeleteMaps" , this.generateConfig.getLogicDeleteMaps());// 删除标识字段映射
			writer = new OutputStreamWriter(fos);
			template.process(parameters , writer);
			writer.flush();
		} catch (Exception e) {
			throw e;
		} finally {
			if(fos != null) {
				try {
					fos.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if(writer != null) {
				try {
					writer.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return tempFile;
	}
	
	/**
	 * 添加模板参数
	 *
	 * @param data
	 * @param parameters
	 */
	public void addParams(String key , Object value) {
		this.parameters.put(key , value);
	}
	
	/**
	 * 设置freemarker版本
	 *
	 * @return
	 */
	public static Version getVersion() {
		return version;
	}
	
	private static void setFreeMarkerCfg(Configuration freeMarkerCfg) {
		CodeGenerateUtil.freeMarkerCfg = freeMarkerCfg;
		CodeGenerateUtil.freeMarkerCfg.setEncoding(Locale.CHINA , "UTF-8");
	}
	
	private void loadTemplateFile(String templateName) throws IOException {
		File dir = new File(this.generateConfig.getTemplateDir());
		freeMarkerCfg.setDirectoryForTemplateLoading(dir);
		freeMarkerCfg.setObjectWrapper(new DefaultObjectWrapper(version));
		try {
			freeMarkerCfg.clearTemplateCache();
			template = freeMarkerCfg.getTemplate(templateName);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
